/*
 * Copyright 2020-2025 the original author or authors.
 * You cannot use this file unless authorized by the author.
 */
package org.ipig.commons.codec.kryo;


import org.ipig.commons.codec.CodecService;
import org.ipig.commons.helper.AssertHelper;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.pool.KryoCallback;
import com.esotericsoftware.kryo.pool.KryoFactory;
import com.esotericsoftware.kryo.pool.KryoPool;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 抽象kryo编码解码服务实现
 *
 * @author <a href="mailto:comchnts@163.com">chinats</a>
 * @version $Id: AbstractKryoCodecService.java 3 2020-01-21 09:36:33Z ts <comchnts@163.com> $
 * @since 1.0
 */
public abstract class AbstractKryoCodecService implements CodecService {
    /**
     * 实例池
     */
    protected final KryoPool pool;

    /**
     * structure
     */
    protected AbstractKryoCodecService() {
        KryoFactory factory = new KryoFactory() {
            public Kryo create() {
                Kryo kryo = new Kryo();
                setKryoInstance(kryo);
                return kryo;
            }
        };
        pool = new KryoPool.Builder(factory).softReferences().build();
    }

    /**
     * @param object       object
     * @param outputStream OutputStream
     * @throws IOException
     */
    @Override
    public void encode(final Object object, OutputStream outputStream) throws IOException {
        AssertHelper.notNull(object, "不能编码null对象");
        AssertHelper.notNull(outputStream, "'outputSteam'不能为null");
        final Output output = (outputStream instanceof Output ? (Output) outputStream : new Output(outputStream));
        this.pool.run(new KryoCallback<Object>() {
            /**
             * @param kryo
             * @return
             * @see com.esotericsoftware.kryo.pool.KryoCallback#execute(com.esotericsoftware.kryo.Kryo)
             */
            public Object execute(Kryo kryo) {
                doEncode(kryo, object, output);
                return Void.class;
            }

        });
        output.close();
    }

    /**
     * @param object Object
     * @return byte[]
     * @throws IOException
     */
    @Override
    public byte[] encode(Object object) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        encode(object, bos);
        byte[] bytes = bos.toByteArray();
        bos.close();
        return bytes;
    }

    /**
     * @param inputStream InputStream
     * @param type        Class<T>
     * @return <T>
     * @throws IOException IOException
     */
    @Override
    public <T> T decode(InputStream inputStream, final Class<T> type) throws IOException {
        AssertHelper.notNull(inputStream, "'inputStream'不能为null");
        AssertHelper.notNull(type, "'type' 不能为null");
        final Input input = (inputStream instanceof Input ? (Input) inputStream : new Input(inputStream));
        T result = null;
        try {
            result = this.pool.run(new KryoCallback<T>() {
                /**
                 * @param kryo
                 * @return
                 * @see com.esotericsoftware.kryo.pool.KryoCallback#execute(com.esotericsoftware.kryo.Kryo)
                 */
                public T execute(Kryo kryo) {
                    return doDecode(kryo, input, type);
                }

            });
        } finally {
            input.close();
        }
        return result;
    }

    /**
     * @param bytes byte[]
     * @param type  Class<T>
     * @return <T>
     * @throws IOException IOException
     */
    @Override
    public <T> T decode(byte[] bytes, Class<T> type) throws IOException {
        AssertHelper.notNull(bytes, "'bytes'不能为null");
        final Input input = new Input(bytes);
        try {
            return decode(input, type);
        } finally {
            input.close();
        }
    }

    /**
     * 编码
     *
     * @param kryo   Kryo
     * @param object Object
     * @param output Output
     */
    protected abstract void doEncode(Kryo kryo, Object object, Output output);

    /**
     * 解码
     *
     * @param kryo  Kryo
     * @param input Input
     * @param type  Class<T>
     * @return <T>
     */
    protected abstract <T> T doDecode(Kryo kryo, Input input, Class<T> type);

    /**
     * 设置Kryo实例
     *
     * @param kryo Kryo
     */
    protected abstract void setKryoInstance(Kryo kryo);
}
